package top.cardone.validator;

import com.google.common.collect.Maps;
import lombok.Setter;
import org.apache.commons.collections.MapUtils;
import top.cardone.context.ApplicationContextHolder;
import top.cardone.core.util.func.Func4;

import java.util.Collection;
import java.util.Map;

/**
 * @author yao hai tao
 * @date 2016/9/19
 */
public class ValidatorImpl implements top.cardone.validator.Validator {
    @Setter
    private String beanIdSuffix = "ValidatorFunc";
    @Setter
    private String rulesKeyName = "rules";
    @Setter
    private String messagesKeyName = "messages";

    @Override
    public Map<String, Object> validate(Map<String, Object> rules, Map<String, Object> messages, Map<String, Object> map) {
        Map<String, Object> errorMap = Maps.newHashMap();

        if (MapUtils.isEmpty(rules)) {
            return errorMap;
        }

        for (Map.Entry<String, Object> ruleEntry : rules.entrySet()) {
            if (!(ruleEntry.getValue() instanceof Map)) {
                continue;
            }

            Map<String, Object> ruleMap = (Map<String, Object>) ruleEntry.getValue();

            if (MapUtils.isEmpty(ruleMap)) {
                continue;
            }

            for (Map.Entry<String, Object> valEntry : ruleMap.entrySet()) {
                Object message = null;

                Object messagesObject = messages.get(ruleEntry.getKey());

                if (messagesObject instanceof Map) {
                    message = MapUtils.getObject((Map<String, Object>) messagesObject, valEntry.getKey());
                }

                String beanId = valEntry.getKey() + beanIdSuffix;

                Func4<Map<String, String>, Map<String, Object>, String, Object, Object> func4 = ApplicationContextHolder.getBean(Func4.class, beanId);

                if (func4 == null) {
                    continue;
                }

                Map<String, String> itemErrorMap = func4.func(map, ruleEntry.getKey(), valEntry.getValue(), message);

                if (MapUtils.isNotEmpty(itemErrorMap)) {
                    errorMap.putAll(itemErrorMap);

                    break;
                }
            }
        }

        return errorMap;
    }

    @Override
    public Map<String, Object> validate(Map<String, Object> validatorMap, Map<String, Object> data) {
        Map<String, Object> errorMap = null;

        if (validatorMap.containsKey(this.rulesKeyName) && validatorMap.containsKey(this.messagesKeyName)) {
            Map<String, Object> rules = Maps.newHashMap((Map<String, Object>) validatorMap.get(this.rulesKeyName));
            Map<String, Object> messages = Maps.newHashMap((Map<String, Object>) validatorMap.get(this.messagesKeyName));

            errorMap = this.validate(rules, messages, data);

            if (MapUtils.isNotEmpty(errorMap)) {
                return errorMap;
            }
        }

        for (Map.Entry<String, Object> validatorEntry : validatorMap.entrySet()) {
            if (this.rulesKeyName.equals(validatorEntry.getKey()) || this.messagesKeyName.equals(validatorEntry.getKey()) || validatorEntry.getValue() instanceof String) {
                continue;
            }

            Object itemData = data.get(validatorEntry.getKey());

            Map<String, Object> itemValidatorMap = (Map<String, Object>) validatorEntry.getValue();

            if (itemData instanceof Collection) {
                Collection<Object> objectList = (Collection<Object>) itemData;

                for (Object object : objectList) {
                    Map<String, Object> newData = top.cardone.context.util.MapUtils.toMap(object, "datas", "data");

                    errorMap = this.validate(itemValidatorMap, newData);

                    if (MapUtils.isNotEmpty(errorMap)) {
                        return errorMap;
                    }
                }
            } else {
                Map<String, Object> newData = top.cardone.context.util.MapUtils.toMap(itemData, "datas", "data");

                errorMap = this.validate(itemValidatorMap, newData);
            }
        }

        return errorMap;
    }
}